﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Management;
using System.Collections;
using Microsoft.Win32;

namespace PennAppsExperimental
{
    public class OperationToken
    {
        private OperationToken() { }
        private string uninstallationString { get; set; }
        public enum UninstallationMode
        {
            Force,
            Natural
        }
        public bool Uninstall(UninstallationMode uninstallationMode)
        {
            bool uninstalled;
            try
            {
                var uninstallCommand = uninstallationString.Replace("\"", ""); //Replace <">
                string uninstallArguments = "";
                string uninstallAssembly = "";

                if (uninstallCommand.ToLowerInvariant().StartsWith("rundll32"))
                {
                    uninstallAssembly = "rundll32.exe";
                    uninstallArguments = uninstallCommand.Replace("rundll32.exe", "").Trim();
                }
                else if (!uninstallCommand.Contains("/")) // if uninstall command is a file path without any parameter
                {
                    uninstallAssembly = uninstallCommand;
                }
                else//if file path has parameter or it is a relative path (for example msiexec.exe or cmd.exe)
                {
                    string[] uninstallArgumentsArray = uninstallCommand.Split(new string[] { " /" }, StringSplitOptions.RemoveEmptyEntries); // Split for any parameters
                    if (uninstallArgumentsArray.Count() > 1) // If 
                    {
                        for (int count = 1; count < uninstallArgumentsArray.Count(); count++)//building parameter string
                        {
                            uninstallArguments += "/" + uninstallArgumentsArray[count];
                        }
                    }
                    uninstallAssembly = uninstallArgumentsArray[0];// file name or path of the uninstall assembly are always on position 0 of the array
                }

                if (!string.IsNullOrWhiteSpace(uninstallAssembly)) //Do not execute process if no uninstall assembly path was found
                {
                    Process uninstallProcess = new Process();
                    uninstallProcess.StartInfo = new ProcessStartInfo();
                    uninstallProcess.StartInfo.FileName = uninstallAssembly;

                    if (uninstallCommand.ToLowerInvariant().StartsWith("rundll32") == false)
                    {
                        uninstallArguments = uninstallArguments + " /qn";
                    }
                    uninstallProcess.StartInfo.Arguments = uninstallArguments;
                    uninstallProcess.StartInfo.UseShellExecute = false;
                    if (uninstallationMode == UninstallationMode.Force)
                    {
                        process.Kill();
                    }
                    uninstallProcess.Start();
                    uninstallProcess.WaitForExit();

                    uninstalled = !ProductExists(process.MainModule.FileName, process.ProcessName);
                }
                else
                {
                    uninstalled = false;//Not executed because no uninstall assembly path is avaible
                }
            }
            catch (Exception)
            {
                uninstalled = false;//Unknown error occured
                // rundll32.exe dfshim.dll,ShArpMaintain GitHub.application, Culture=neutral, PublicKeyToken=317444273a93ac29, processorArchitecture=x86
                //Process uninstallProcess = new Process();
                //uninstallProcess.StartInfo = new ProcessStartInfo();
                //uninstallProcess.StartInfo.FileName = "rundll32.exe";
                //uninstallProcess.StartInfo.Arguments = "dfshim.dll,ShArpMaintain GitHub.application, Culture=neutral, PublicKeyToken=317444273a93ac29, processorArchitecture=x86";
                //uninstallProcess.Start();
            }
            return uninstalled;//return if the kickoff for the uninstall process was successfull
        }
        Process process;

        /// <summary>
        /// Returns the first product that contains a certain process
        /// </summary>
        /// <param name="processName"></param>
        /// <returns></returns>
        public static OperationToken InferProductFromProcess(Process process)
        {
            StringBuilder sb = new StringBuilder();
            var queryString = "SELECT * FROM Win32_Process Where ProcessId = '" + process.Id + "'";
            OperationToken operationToken = new OperationToken() { process = process };
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(queryString);
            var targets = searcher.Get().Cast<ManagementObject>();
            if (targets.Count() == 0)
            {
                return null;
            }
            var target = targets.First();
            var execPath = target["ExecutablePath"].ToString();
            //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
            string uninstallString = null;
            if ((uninstallString = probeValueUninstallation(Registry.LocalMachine, execPath, process.ProcessName)) == null)
            {
                if ((uninstallString = probeValueUninstallation(Registry.Users, execPath, process.ProcessName)) == null)
                {
                    if ((uninstallString = probeValueUninstallation(Registry.CurrentUser, execPath, process.ProcessName)) != null)
                    {
                        operationToken.uninstallationString = uninstallString;
                    }
                }
                else
                {
                    operationToken.uninstallationString = uninstallString;
                }
            }
            else
            {
                operationToken.uninstallationString = uninstallString;
            }
            if (operationToken.uninstallationString != null) return operationToken;
            else return null;
        }

        private static bool ProductExists(string fileLocation, string procName)
        {
            if (string.IsNullOrEmpty(fileLocation) || string.IsNullOrEmpty(procName))
            {
                return false;
            }
            bool found = false;
            if ((probeValueUninstallation(Registry.LocalMachine, fileLocation, procName)) == null)
            {
                if (probeValueUninstallation(Registry.Users, fileLocation, procName) == null)
                {
                    if ((probeValueUninstallation(Registry.CurrentUser, fileLocation, procName)) != null)
                    {
                        found = true;
                    }
                }
                else
                {
                    found = true;
                }
            }
            else
            {
                found = true;
            }
            return found;
        }

        private static string probeValueUninstallation(RegistryKey hive, string queryValue, string secondaryQueryValue)
        {
            RegistryKey key = null;
            try
            {
                key = hive.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("Uninstall");
            }
            catch
            {
                return null;
            }
            string candidate = "";
            foreach (string keyName in key.GetSubKeyNames())
            {
                var subkey = key.OpenSubKey(keyName);
                if (subkey.GetValueNames().Contains("UninstallString") && subkey.GetValueNames().Contains("InstallLocation"))
                {
                    if (queryValue.StartsWith(subkey.GetValue("InstallLocation").ToString()) && subkey.GetValue("InstallLocation").ToString() != "")
                    {
                        if (subkey.GetValue("InstallLocation").ToString().Length >= candidate.Length)
                        {
                            candidate = subkey.GetValue("UninstallString").ToString();
                        }
                    }
                }
                else if (subkey.GetValueNames().Contains("DisplayName") && subkey.GetValueNames().Contains("UninstallString"))
                {
                    subkey.GetValue("UninstallString").ToString();
                    if (subkey.GetValue("DisplayName").ToString() == secondaryQueryValue)
                    {
                        return candidate;
                    }
                }

            }
            return candidate.Length != 0 ? candidate : null;
        }
    }
}
